/*
 ============================================================================
 Name        : mySyslogd.c
 Author      : Shaohong
 Version     : see http://code.google.com/p/shaohong-aalto-study/ revision logs for details
 Description   : Shaohong's assignment #2 for S-38.3600 Unix Application programming
 ============================================================================
 */

#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/syslog.h>
#include <stdbool.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/resource.h>
#include <stdarg.h>
#include <assert.h>
#include "sysLogger.h"
#include "errorLogging.h"

#define FIFO_PERMS (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)

void gracefulExit(void) {

	// remove the FIFO
	unlink(FIFO_NAME);

	// close the log file
	closeLogFile();
}

/*
 *The SIG_TERM handler for the main process.
 */
void sigTermHandler(int sigNo) {
	gracefulExit();
}


/*
 * helper function to install signal handler for the given signal
 */
void setupSigHandler(int sigNo, void(*sigHandler)(int)) {
	struct sigaction sigIntAction;

	//we won't block any during our execution
	sigemptyset(&sigIntAction.sa_mask);

	//no special options
	sigIntAction.sa_flags = 0;

	//set the signal handler
	sigIntAction.sa_handler = sigHandler;

	assert((sigaction(sigNo, &sigIntAction, NULL)) == 0);
}

void daemonize(const char *cmd);

int main(int argc, char ** argv) {
	// make this program a daemon
	daemonize(argv[0]);

	char buffer[1024];
	log_msg("%s started", argv[0]);

	// open the log file for appending
	openLogFile();

	//make the FIFO
	if (-1 == mkfifo(FIFO_NAME, FIFO_PERMS)) {
		if (errno != EEXIST) {
			log_quit("failed to create named pipe!");
		}

		if (errno == EEXIST) {
			log_quit("PIPE exists! \"%s\" is already running!\n", argv[0]);
		}

		log_quit("failed to create FIFO");
	}

	log_msg("FIFO %s created!", FIFO_NAME);

	//open the FIFO for reading
	int fifoFD = open(FIFO_NAME, O_RDWR);
	log_msg("FIFO %s opened for READING!", FIFO_NAME);

	if (-1 == fifoFD) {
		log_quit("Failed to open FIFO in read-only mode");
	}

	// setup the graceful exit
    if (0 != atexit(gracefulExit)) {
    	log_quit("cannot set exit function\n");
        exit(EXIT_FAILURE);
    }

    //set up the signal handler
    setupSigHandler(SIGTERM, sigTermHandler);

	// block reading from the FIFO
	while (true) {
//		fprintf(stderr, "blocking reading from FIFO\n");
		memset(buffer, 0, sizeof(buffer));

		int nBytes = read(fifoFD, buffer, sizeof(buffer)-1);
//		fprintf(stderr, "read from pipe:%s\n", buffer);

		if (-1 != nBytes) {
			// write the log to output file
			logMsg(buffer);
		} else {
			sprintf(buffer, "read returns %d\n", nBytes);
			logMsg(buffer);
//			fprintf(stderr, "read returns %d\n", nBytes);
		}
	}

	gracefulExit();

	return EXIT_SUCCESS;
}



/**
 * code borrowed from Chapter 13 of Stevens and Rago's APUE
 */
void
daemonize(const char *cmd)
{
    int                 i, fd0, fd1, fd2;
    pid_t               pid;
    struct rlimit       rl;
    struct sigaction    sa;
    /*
     * Clear file creation mask.
     */
    umask(0);

    /*
     * Get maximum number of file descriptors.
     */
    if (getrlimit(RLIMIT_NOFILE, &rl) < 0) {
        err_quit("%s: can't get file limit", cmd);
    }

    /*
     * Become a session leader to lose controlling TTY.
     */
    if ((pid = fork()) < 0)
        err_quit("%s: can't fork", cmd);
    else if (pid != 0) /* parent */
        exit(0);
    setsid();

    /*
     * Ensure future opens won't allocate controlling TTYs.
     */
    sa.sa_handler = SIG_IGN;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = 0;
    if (sigaction(SIGHUP, &sa, NULL) < 0)
        err_quit("%s: can't ignore SIGHUP");
    if ((pid = fork()) < 0)
        err_quit("%s: can't fork", cmd);
    else if (pid != 0) /* parent */
        exit(0);

    /*
     * Change the current working directory to the root so
     * we won't prevent file systems from being unmounted.
     */
    if (chdir("/") < 0)
        err_quit("%s: can't change directory to /");

    /*
     * Close all open file descriptors.
     */
    if (rl.rlim_max == RLIM_INFINITY)
        rl.rlim_max = 1024;
    for (i = 0; i < rl.rlim_max; i++)
        close(i);

    /*
     * Attach file descriptors 0, 1, and 2 to /dev/null.
     */
    fd0 = open("/dev/null", O_RDWR);
    fd1 = dup(0);
    fd2 = dup(0);

    /*
     * Initialize the log file.
     */
    openlog(cmd, LOG_CONS, LOG_DAEMON);
    if (fd0 != 0 || fd1 != 1 || fd2 != 2) {
        syslog(LOG_ERR, "unexpected file descriptors %d %d %d",
          fd0, fd1, fd2);
        exit(1);
    }
}
